/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mypopsy.widget.adapter;

import ohos.agp.components.*;
import ohos.app.Context;
import java.util.ArrayList;
import java.util.List;

public abstract class BaseListAdapter<T> extends BaseItemProvider implements CRUD<T> {

    private final Context context;
    List<T> mData;
    int mLayoutResId;

    /**
     * Item点击监听
     */
    private OnListItemClickListener<T> listener;

    /**
     * 常规列表重写该方法
     *
     * @param context     context
     * @param list       数据源
     * @param layoutResId 布局文件
     * @param listener    Item点击回调
     */
    public BaseListAdapter(Context context, List<T> list, int layoutResId,
                           OnListItemClickListener<T> listener) {
        this.context = context;
        this.mData = list == null ? new ArrayList<T>() : list;
        this.mLayoutResId = layoutResId;
        this.listener = listener;
    }

    /**
     * Item点击事件
     *
     * @param listener listener
     */
    public void setOnListItemClickListener(OnListItemClickListener<T> listener) {
        this.listener = listener;
    }

    public void onBind(final SuperViewHolder viewHolder, final int layoutPosition, T data) {
        viewHolder.itemView.setClickedListener(v -> {
            if (listener != null) {
                listener.onItemClick(data, layoutPosition);
            }
        });
        onBindData(viewHolder, layoutPosition, data);
    }

    public abstract void onBindData(SuperViewHolder holder, int layoutPosition, T data);

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public T getItem(int i) {
        return mData.get(i);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public Component getComponent(int position, Component component,
                                  ComponentContainer componentContainer) {
        final Component cpt;
        if (component == null) {
            cpt = LayoutScatter.getInstance(context).parse(mLayoutResId, null, false);
        } else {
            cpt = component;
        }
        SuperViewHolder superViewHolder = SuperViewHolder.get(component, cpt);
        if (position < mData.size()) {
            onBind(superViewHolder, position, mData.get(position));
        }

        return cpt;
    }

    /**
     * ------------------------------------ CRUD ------------------------------------
     */

    @Override
    public void add(T item) {
        mData.add(item);
        int location = mData.size() - 1;
        notifyDataSetItemInserted(location);
    }

    @Override
    public void add(int location, T item) {
        mData.add(location, item);
        notifyDataSetItemInserted(location);
    }

    @Override
    @Deprecated
    public void insert(int location, T item) {
        add(location, item);
    }

    @Override
    public void addAll(List<T> items) {
        if (items == null || items.isEmpty()) {
//            Log.w(TAG, "addAll: The list you passed contains no elements.");
            return;
        }
        int location = getCount();
        mData.addAll(items);
        notifyDataSetItemRangeInserted(location, items.size());
    }

    @Override
    public void addAll(int location, List<T> items) {
        if (items == null || items.isEmpty()) {
//            Log.w(TAG, "addAll: The list you passed contains no elements.");
            return;
        }
        if (location < 0 || location > getCount()) {
//            Log.w(TAG, "addAll: IndexOutOfBoundsException");
            return;
        }
        mData.addAll(location, items);
        notifyDataSetItemRangeInserted(location, items.size());
    }

    @Override
    public void remove(T item) {
        if (contains(item)) {
            remove(mData.indexOf(item));
        }
    }

    @Override
    public void remove(int location) {
        mData.remove(location);
        notifyDataSetItemRemoved(location);
    }

    @Override
    public void removeAll(List<T> items) {
        mData.removeAll(items);
        notifyDataChanged();
    }

    @Override
    public void retainAll(List<T> items) {
        mData.retainAll(items);
        notifyDataChanged();
    }

    @Override
    public void set(T oldItem, T newItem) {
        set(mData.indexOf(oldItem), newItem);
    }

    @Override
    public void set(int location, T item) {
        mData.set(location, item);
        notifyDataSetItemChanged(location);
    }

    @Override
    public void replaceAll(List<T> items) {
        if (mData == items) {
            notifyDataChanged();
            return;
        }
        if (items == null || items.isEmpty()) {
            clear();
            return;
        }
        if (mData.isEmpty()) {
            addAll(items);
        } else {
            int start = 0;
            int originalSize = getCount();
            int newSize = items.size();
            mData.clear();
            mData.addAll(items);
            if (originalSize > newSize) {
                notifyDataSetItemRangeChanged(start, newSize);
                notifyDataSetItemRangeRemoved(start + newSize, originalSize - newSize);
            } else if (originalSize == newSize) {
                notifyDataSetItemRangeChanged(start, newSize);
            } else {
                notifyDataSetItemRangeChanged(start, originalSize);
                notifyDataSetItemRangeInserted(start + originalSize, newSize - originalSize);
            }
        }
    }

    @Override
    public boolean contains(T item) {
        return mData.contains(item);
    }

    @Override
    public boolean containsAll(List<T> items) {
        return mData.containsAll(items);
    }

    @Override
    public void clear() {
        int count = getCount();
        if (count > 0) {
            mData.clear();
            notifyDataSetItemRangeRemoved(0, count);
        }
    }

    public List<T> getData() {
        return mData;
    }
}
